#pragma once
// 需要设置套接字，监听，accpet等待

#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
#include "errno.hpp"

#define DEFAULEBACKLOGS 50

class Sock
{
public:
    static int SetSocket()
    {
        int fd = socket(AF_INET, SOCK_STREAM, 0);

        if (fd < 0)
        {
            logMessage(FATAL);
            exit(SOCKET_ERRON);
        }

        //允许该机器不同的ip创建同一端口
        int opt = 1;
        socklen_t opts = sizeof(opt);
        int n  = setsockopt(fd,SOL_SOCKET, SO_REUSEADDR  , &opt , opts);
        if(n == -1)
        {
            logMessage(FATAL);
            exit(SETSOCKOPT_ERRON);
        }

        n = setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,&opt,opts);
        if(n == -1)
        {
            logMessage(FATAL);
            exit(SETSOCKOPT_ERRON);
        }

        return fd;
    }

    //将某个ip和端口号与套接字相连接，客户端可以不显示bind链接
    // 正常运行 -> OS 发现客户端的套接字没有连接ip和端口，->链接ip和端口
    static void  Bind(int socket,std::string ip,uint16_t port)
    {
        struct sockaddr_in address;
        address.sin_port = htons(port);
        //address.sin_addr.s_addr = inet_addr(ip.c_str());
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        socklen_t solent = sizeof(address);
    
        int n = bind(socket,(const sockaddr*)&address,solent);
        if(n == -1)
        {
            logMessage(FATAL);
            exit(BIND_ERRON);
        }

        std::cout << "Bind sucess" << std::endl;
    }
    // 全底层队列的大小暂时设为50
    static void Listen(int listensockfd, int backlog = DEFAULEBACKLOGS)
    {
        int n = listen(listensockfd, backlog);

        if (n < 0)
        {
            logMessage(FATAL);
            exit(LISTEN_ERRON);
        }

        std::cout << "Listen sucess" << std::endl;
    }

    // Accpet链接套接字,只要建立建立成功就可以开始通信了
    static int Accpet(int listensock , std::string& clinetip,uint16_t& clinetport)
    {
        // 返回最终sock
        struct sockaddr_in address;
        memset(&address, 0, sizeof(address));
        socklen_t addsize = sizeof(address);
        int sock = accept(listensock, (struct sockaddr *)&address, &addsize);
        
        //这个结构体由操作系统填充
       
        // 如果该套接字大于0，则表示该套接字已经完全准备好了，可以开始通信，sock < 0 链接失败也没关系
        if (sock > 0)
        {
             clinetip = inet_ntoa(address.sin_addr);
             clinetport = ntohs(address.sin_port);
            return sock;
        }
        else 
        return -1;
    }
};